package com.grancalavera.flixel.spaceinvaders
{
import org.flixel.FlxGroup;
import org.flixel.FlxSprite;
import org.flixel.FlxG;
import org.flixel.FlxPoint;
import com.grancalavera.flixel.Utils;

public class Wave extends FlxGroup
{

    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    //  HORIZONTAL_DISPLACEMENT
    //----------------------------------
    /**
     * How much to change the horizontal position of the wave
     */
    public static const HORIZONTAL_DISPLACEMENT:int = INVADER_WIDTH / 4;

    //----------------------------------
    //  VERTICAL_DISPLACEMENT
    //----------------------------------
    /**
     * How much to change the horizontal position of the wave
     */
    public static const VERTICAL_DISPLACEMENT:int = INVADER_HEIGHT / 2;

    //----------------------------------
    //  ROWS
    //----------------------------------
    /**
     * The number of invader rows
     */
    public static const ROWS:int = 6;

    //----------------------------------
    //  COLUMNS
    //----------------------------------
    /**
     * The number of invder columns
     */
    public static const COLUMNS:int = 6;

    //----------------------------------
    //  ROW_GAP
    //----------------------------------
    /**
     * The gap between rows
     */
    public static const ROW_GAP:int = 0;

    //----------------------------------
    //  COLUMN_GAP
    //----------------------------------
    /**
     * The gap between columns
     */
    public static const COLUMN_GAP:int = 10;

    //----------------------------------
    //  INVADER_WIDTH
    //----------------------------------
    /**
     * Invader width
     */
    public static const INVADER_WIDTH:int = 44;

    //----------------------------------
    //  INVADER_HEIGHT
    //----------------------------------
    /**
     * Invader height
     */
    public static const INVADER_HEIGHT:int = 44;

    //----------------------------------
    //  INVADER_SKIN_1
    //----------------------------------
    [Embed(source="/../assets/png/invader_1.png")]
    /**
     * Skin 
     */
    public static const INVADER_SKIN_1:Class;

    //----------------------------------
    //  INVADER_SKIN_2
    //----------------------------------
    [Embed(source="/../assets/png/invader_2.png")]
    /**
     * Skin 
     */
    public static const INVADER_SKIN_2:Class;

    //----------------------------------
    //  INVADER_SKIN_3
    //----------------------------------
    [Embed(source="/../assets/png/invader_3.png")]
    /**
     * Skin 
     */
    public static const INVADER_SKIN_3:Class;

    //----------------------------------
    //  INVADER_BY_ROW
    //----------------------------------
    /**
     * an array of <code>FlxSprite<code> representing each of the invader classes 
     * to be instantiated by row
     */
    public static const INVADER_BY_ROW:Array = 
    [
        INVADER_SKIN_1,
        INVADER_SKIN_2,
        INVADER_SKIN_3,
        INVADER_SKIN_1,
        INVADER_SKIN_2,
        INVADER_SKIN_3
    ];

    //--------------------------------------------------------------------------
    //
    // Constructor
    //
    //--------------------------------------------------------------------------

    /**
     * Creates a new invader wave.
     */
    public function Wave()
    {
        super();
        
        var invader:Invader;
        var columnWidth:int = COLUMN_GAP + INVADER_WIDTH;
        var rowHeight:int = ROW_GAP + INVADER_HEIGHT;
        columns = [];
        aliens = [];
        
        for (var i:int = 0; i < COLUMNS; i++)
        {
            columns[i] = new FlxGroup();
            
            for (var j:int = 0; j < ROWS; j++)
            {
                invader = new Invader(
                    i * columnWidth,
                    j * rowHeight,
                    INVADER_BY_ROW[j]
                );
                
                if (i % 2 == 0)
                    invader.flip();
                    
                columns[i].add(invader);
                aliens.push(invader);
            }
            
            add(columns[i]);
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  columns
    //----------------------------------
    /**
     *  @private
     */
    private var columns:Array;

    //----------------------------------
    //  aliens
    //----------------------------------
    /**
     *  @private
     */
    private var aliens:Array;
    
    //----------------------------------
    //  advancing
    //----------------------------------
    /**
     *  @private
     */
    private var advancing:Boolean;

    //----------------------------------
    //  turning
    //----------------------------------
    /**
     *  @private
     */
    private var turning:Boolean;
    
    //----------------------------------
    //  direction
    //----------------------------------
    /**
     *  @private
     */
    private var direction:int = 1;

    //----------------------------------
    //  elapsedSinceLast
    //----------------------------------
    /**
     *  @private
     */
    private var elapsedSinceLast:Number = 0;

    //----------------------------------
    //  updateInterval
    //----------------------------------
    /**
     *  @private
     */
    private var updateInterval:Number;

    //----------------------------------
    //  minUpdateInterval
    //----------------------------------
    /**
     *  @private
     */
    private var minUpdateInterval:Number = 0.02;

    //----------------------------------
    //  maxUpdateInterval
    //----------------------------------
    /**
     *  @private
     */
    private var maxUpdateInterval:Number = 0.25;

    //----------------------------------
    //  updateIntervalIncrements
    //----------------------------------
    /**
     *  @private
     */
    private var updateIntervalIncrements:Number = 0.01;
    
    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  defaultWidth
    //----------------------------------
    /**
     * Returns the initial wave width
     */
    public function get defaultWidth():Number
    {
        return (INVADER_WIDTH * COLUMNS) + (COLUMN_GAP * (COLUMNS - 1));
    }

    //--------------------------------------------------------------------------
    //
    //  Overridden Methods - Flx
    //
    //--------------------------------------------------------------------------

    override public function update():void
    {
        if (countLiving() < 1)
            return;
            
        super.update();
        updateInterval = getUpdateInterval();
        
        var invader:Invader;
        var column:FlxGroup;
        
        if (elapsedSinceLast > updateInterval)
        {   

            if (advancing)
            {
                advancing = false;
                turning = true;
                direction = direction == 1 ? -1 : 1;
                for each (invader in aliens)
                    if (!invader.dead)
                    {
                        invader.y += VERTICAL_DISPLACEMENT;
                        invader.flip();
                    }
            }
            else
            {
                turning = false
                for each (invader in aliens)
                    if (!invader.dead)
                    {
                        invader.x += HORIZONTAL_DISPLACEMENT * direction;
                        invader.flip();
                    }
            }
            
            FlxG.play(Sounds.WAVE_MOVE, 0.4);
            elapsedSinceLast = 0;
        }

        elapsedSinceLast += FlxG.elapsed;
    }
    

    override public function countLiving():int
    {
        var living:int;
        var column:FlxGroup;
        for each (column in members)
            living += column.countLiving();
        return living;        
    }
    
    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  advance
    //----------------------------------
    /**
     * Makes all the wave move one step down and change direction
     */
    public function advance():void
    {
        if (turning)
            return;
        advancing = true;
    }

    //----------------------------------
    //  killColumn
    //----------------------------------
    /**
     * Kills a complete column
     */
    public function killColumn(columnNumber:Number):void
    {
        columns[columnNumber].kill();
    }
    
    //----------------------------------
    //  getFiringPoint
    //----------------------------------
    /**
     * Returs a random firing point
     */
    public function getFiringInvader():FlxSprite
    {
        if (countLiving() <= 0)
            return null;
            
        var index:int;
        var column:FlxGroup;
        while (!column)
        {
            index = Utils.randRange(0, COLUMNS - 1);
            if (FlxGroup(columns[index]).countLiving() > 0)
                column = columns[index]
        }
        
        // find living invader at the bottom of the column
        var bottomInvader:FlxSprite = column.members[0];
        var invader:FlxSprite;
        var bottomY:Number = 0;
        
        for each (invader in column.members)
            if (!invader.dead && (invader.y >= bottomInvader.y))
                bottomInvader = invader;

        return bottomInvader;
    } 

    //----------------------------------
    //  getUpdateInterval
    //----------------------------------
    /**
     * @private
     */
    private function getUpdateInterval():Number
    {
        return Math.max(
            minUpdateInterval, 
            Math.min(
                maxUpdateInterval, 
                (countLiving() * updateIntervalIncrements)
            )
        );
    }

}
}